home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Magnum One
/
Magnum One (Mid-American Digital) (Disc Manufacturing).iso
/
d12
/
cgazv5n5.arc
/
PROFILER.C
< prev
next >
Wrap
C/C++ Source or Header
|
1991-09-23
|
33KB
|
1,113 lines
/*--- PROFILER.C ---------------------- Listing 1 ---------
* OS/2 1.x Execution Profiler
* Author : Mark Florence, San Francisco Canyon Co, Inc.
* History : 01/27/91 Initial Implementation
* Compiler : MS C6.00a (Large model)
*
* (c) C Gazette. Source may be used if authorship and publi-
* cation are acknowledged. Object files may be used freely.
*-------------------------------------------------------*/
#define INCL_DOS
#define INCL_DOSTRACE
#include <os2.h>
#include <dos.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <io.h>
#include <process.h>
#include <malloc.h>
#include <time.h>
#include "Profiler.h"
// Constants
#define SAMPLE_INTERVAL 10L // Nominal sample interval
#define STACK_SIZE 8192 // Stack size for threads
#define MAX_SELECTORS 8192 // Maximum selectors
#define NOMINAL_BUCKET_SIZE 64 // Nominal size of buckets
#define MINIMUM_BUCKET_SIZE 32 // Minimum size of buckets
// DosPTrace Commands
// Get program's registers:
#define PTRACE_CMD_READ_REGISTERS 0x0003
// (Re)start program
#define PTRACE_CMD_GO 0x0007
// Stop program
#define PTRACE_CMD_STOP 0x000A
// Convert index to selector
#define PTRACE_CMD_NUMTOSEL 0x000D
// Get DLL name
#define PTRACE_CMD_GET_LIBNAME 0x0010
// DosPTrace Events
#define PTRACE_EVENT_SUCCESS 0 // Successful return
#define PTRACE_EVENT_ERROR -1 // Error (bad command etc)
#define PTRACE_EVENT_SIGNAL -2 // Breakpoint at signal
#define PTRACE_EVENT_SINGLE_STEP -3 // Single-step breakpoint
#define PTRACE_EVENT_BREAKPOINT -4 // Breakpoint
#define PTRACE_EVENT_DYING -6 // Program ending
#define PTRACE_EVENT_GP_FAULT -7 // GP fault detected
#define PTRACE_EVENT_LIBRARY_LOAD -8 // DLL loading
#define PTRACE_EVENT_THREAD_DEAD -10 // Thread ending
// Module (.EXE or .DLL) Function List
// (list of functions in a module)
typedef struct _FLIST // Hungarian: flst, pflst
{ USHORT selv, offv, lenv; // Funct. selector, offset, length
PCHAR pszName; // -->Function name
struct _FLIST *pflstNext; // -->Next function in list
} FLIST, *PFLIST;
// Module (.EXE or .DLL) List
// (list of modules actually loaded)
typedef struct _MLIST // Hungarian: mlst, pmlst
{ PCHAR pszName; // -->Module name
struct _MLIST *pmlstNext; // -->Next module in list
struct _FLIST *pflstFirst; //-->First function in list
} MLIST, *PMLIST;
// Library (.DLL) List
// (list of DLLs for which profiling is required)
typedef struct _LLIST // Hungarian: llst, pllst
{ PCHAR pszName; // -->DLL name
struct _LLIST *pllstNext; // -->Next DLL in list
} LLIST, *PLLIST;
// Activity Buckets
// (one set for each segment in each module)
typedef struct _BUCKETS // Hungarian: bckt, pbckt
{ USHORT segv; // Segment
ULONG ulSegSize; // Segment size
struct _MLIST *pmlst; // -->Module
USHORT cBuckets; // Bucket count
LONG alBucket[0]; // Array of buckets
} BUCKETS, *PBUCKETS;
// Selector-to-Segment Index
// (one for each possible selector)
typedef struct _SELINDEX // Hungarian: selix, pselix
{ PBUCKETS apbckt[MAX_SELECTORS]; // -->Activity buckets
} SELINDEX, *PSELINDEX;
// Global Data
struct
{ PTRACEBUF ptb; // PTrace buffer
PSELINDEX pselix; // -->Selector-to-segment index
PMLIST pmlstFirst; // -->First module loaded
PLLIST pllstFirst; // -->First DLL expected
BOOL fAllDLLs; // TRUE == Profile ALL DLLs
USHORT cbBucket; // Bucket size
LONG lUnknown, lKnown; // Unknown, known activity count
CHAR szResults[CCHMAXPATH]; // Results file
} NEAR glbl;
// Function Declarations
VOID main (int, char **);
VOID NEAR BuildBuckets (PMLIST);
VOID NEAR BuildFunctionList (PMLIST, PCHAR);
VOID NEAR BuildLibraryList (PCHAR);
VOID NEAR BuildModuleList (PCHAR, BOOL);
VOID NEAR BuildSelectorIndex (VOID);
int NEAR ExtractOptions (int, char **);
VOID NEAR FreeDataStructures (VOID);
VOID NEAR PrintResults (VOID);
USHORT NEAR ProfileProgram (PCHAR);
VOID FAR Signaller (PVOID);
VOID NEAR StartProgram (int, char **, int, PCHAR);
// Function: main - Profiler Main Function
VOID main (argc, argv)
int argc; // Argument count
char *argv[]; // Argument values
// Define function data
{
int iarg; // Index to program name
USHORT idErr; // Error return code
CHAR szPgm[CCHMAXPATH]; // Name of program to profile
int tidSignaller; // Signaller thread id
// Extract options from arguments
iarg = ExtractOptions (argc, argv);
// Start program to be profiled
StartProgram (argc, argv, iarg, szPgm);
// Build selector-to-segment index
BuildSelectorIndex ();
// Start signalling thread
tidSignaller = _beginthread (Signaller, NULL,
STACK_SIZE, NULL);
DosSetPrty (PRTYS_THREAD, PRTYC_TIMECRITICAL,
PRTYD_MAXIMUM, tidSignaller);
// Profile program
idErr = ProfileProgram (szPgm);
// Print results
PrintResults ();
// Free all data structures
FreeDataStructures ();
// Return to caller
exit (idErr);
}
// Build Accumulator Buckets for a Module
VOID NEAR BuildBuckets (pmlst)
PMLIST pmlst; // -->Module list entry
// Define function data
{ PBUCKETS pbckt; // -->Accumulator buckets
USHORT segv, selv; // Segment and selector values
USHORT cBuckets; // Count of buckets
USHORT iBucket; // Bucket index
CHAR szCommand[CCHMAXPATH*2]; // EXEHDR command
BOOL fIgnore = TRUE; // TRUE == ignoring EXEHDR line
CHAR szLine[256]; // Line from temporary file
ULONG ulSegSize; // Segment size
CHAR szSegType[32]; // Segment type
FILE *fTempFile; // Temporary file handle
CHAR szTempFile[L_tmpnam]; //Temporary file name
static PCHAR pszHdr =
"no. type address file mem flags";
// Issue EXEHDR command
tmpnam (szTempFile);
sprintf (szCommand, "EXEHDR /NOLOGO %s >%s",
pmlst->pszName, szTempFile);
system (szCommand);
// Open temporary file with EXEHDR results
fTempFile = fopen (szTempFile, "rt");
// Issue an error if the EXEHDR results could not be found
if (fTempFile == 0)
{ printf
("\nWARNING: Unable to open EXEHDR temporary file %s\n",
szTempFile);
return;
}
// Read temporary file with EXEHDR results
// to build buckets for all CODE segments
while (fgets (szLine, sizeof (szLine), fTempFile) != NULL)
// Ignore EXEHDR lines until we get to the interesting part
{ if (fIgnore && (strstr (szLine, pszHdr) != NULL))
{fIgnore = FALSE;
continue;
}
if (fIgnore)
continue;
// Extract segment number and length from the EXEHDR line
if (sscanf (szLine, "%u %s %*s %*s %lx",
&segv, szSegType, &ulSegSize) < 3)
break;
// Ignore non-CODE segments
if (strstr (szSegType, "CODE") == NULL)
continue;
// Convert each segment to a selector
glbl.ptb.cmd = PTRACE_CMD_NUMTOSEL;
glbl.ptb.value = segv;
DosPTrace ((PBYTE) &glbl.ptb);
if (glbl.ptb.cmd != 0)
continue;
selv = glbl.ptb.value;
// Convert selector to an index [0..8191]
selv >>= 3;
// Calculate the number of buckets from the segment size
cBuckets = (USHORT) (ulSegSize / glbl.cbBucket) + 1;
// Allocate a block for the buckets
pbckt = (PBUCKETS) malloc (sizeof (*pbckt)
+ (cBuckets * sizeof (LONG)));
// Issue an error if out of memory
if (pbckt == NULL)
{printf
("\nOut of memory at %s:%d\n", __FILE__, __LINE__);
exit (1);
}
// Chain buckets to the selector-to-segment index
glbl.pselix->apbckt[selv] = pbckt;
// Initialize buckets
pbckt->segv = segv;
pbckt->ulSegSize = ulSegSize;
pbckt->pmlst = pmlst;
pbckt->cBuckets = cBuckets;
// Loop until all temporary file lines read
for (iBucket = 0; iBucket < cBuckets; iBucket++)
pbckt->alBucket[iBucket] = 0;
}
// Close then erase temporary file
fclose (fTempFile);
unlink (szTempFile);
}
// Build List of Functions for a Module
VOID NEAR BuildFunctionList (pmlst, pszMap)
PMLIST pmlst; // -->Module list entry
PCHAR pszMap; // -->Name of map file
// Define function data
{ // Segment, selector, offset values:
USHORT segv, selv, offv;
// Current, last function list entry:
PFLIST pflst, pflstLast = NULL;
CHAR szFunction[CCHMAXPATH]; // Current function name
BOOL fIgnore = TRUE; // TRUE == ignoring MAP line
CHAR szLine[256]; // Line from MAP file
FILE *fMap; // MAP file handle
static PCHAR pszHdr = "Publics by Value";
// Open map file
fMap = fopen (pszMap, "rt");
// Issue an error if the MAP file could not be found
if (fMap == 0)
{printf
("\nWARNING: Unable to open MAP file %s\n", pszMap);
return;
}
// Read MAP file to build FLIST entries for each function
while (fgets (szLine, sizeof (szLine), fMap) != NULL)
// Ignore MAP lines until we get to the interesting part
{if (fIgnore && (strstr (szLine, pszHdr) != NULL))
{fIgnore = FALSE;
if (fgets (szLine, sizeof (szLine), fMap) == NULL)
break;
else continue;
}
if (fIgnore)
continue;
// Extract function details from the MAP line
if (sscanf (szLine, "%4x %*c %4x %s", &segv, &offv,
szFunction) < 3)
break;
// Ignore non-CODE functions
glbl.ptb.cmd = PTRACE_CMD_NUMTOSEL;
glbl.ptb.value = segv;
DosPTrace ((PBYTE) &glbl.ptb);
if (glbl.ptb.cmd != 0)
continue;
selv = glbl.ptb.value >> 3;
if (glbl.pselix->apbckt[selv] == NULL)
continue;
// Allocate a block for the function list entry
pflst = (PFLIST) malloc (sizeof (*pflst));
// Issue an error if out of memory
if (pflst == NULL)
{printf
("\nOut of memory at %s:%d\n", __FILE__, __LINE__);
exit (1);
}
// Allocate a block for the function name
pflst->pszName =
(PCHAR) malloc (strlen (szFunction) + 1);
// Issue an error if out of memory
if (pflst->pszName == NULL)
{printf (
"\nOut of memory at %s:%d\n", __FILE__, __LINE__);
exit (1);
}
// Initialize function list entry
pflst->selv = selv;
pflst->offv = offv;
if (pflstLast != NULL)
{if (pflst->selv == pflstLast->selv)
pflstLast->lenv = pflst->offv - pflstLast->offv;
else pflstLast->lenv = (USHORT)
(glbl.pselix->apbckt[pflstLast->selv]->ulSegSize
- pflstLast->offv);
}
strcpy (pflst->pszName, szFunction);
pflstLast = pflst;
// Link this entry to the function list chain
if (pmlst->pflstFirst == NULL)
{pmlst->pflstFirst = pflst;
pflst->pflstNext = NULL;
}
else
{pflst->pflstNext = pmlst->pflstFirst;
pmlst->pflstFirst = pflst;
}
// Loop until all MAP lines read
}
// Set size of last function, if any
if (pflstLast != NULL)
pflstLast->lenv = (USHORT)
(glbl.pselix->apbckt[pflstLast->selv]->ulSegSize
- pflstLast->offv);
// Close the MAP file
fclose (fMap);
}
// Add Entry to Library List
VOID NEAR BuildLibraryList (pszModule)
PCHAR pszModule; // -->DLL name
// Define function data
{ PLLIST pllst; // -->Library list entry
// Check if ALL specified
if (stricmp (pszModule, "ALL") == 0)
{glbl.fAllDLLs = TRUE;
return;
}
// Allocate a block for the library list entry
pllst = (PLLIST) malloc (sizeof (*pllst));
// Issue an error if out of memory
if (pllst == NULL)
{printf
("\nOut of memory at %s:%d\n", __FILE__, __LINE__);
exit (1);
}
// Allocate a block for the library name
pllst->pszName =
(PCHAR) malloc (strlen (pszModule) + 1);
// Issue an error if out of memory
if (pllst->pszName == NULL)
{printf
("\nOut of memory at %s:%d\n", __FILE__, __LINE__);
exit (1);
}
// Initialize the library list entry
strcpy (pllst->pszName, pszModule);
strupr (pllst->pszName);
pllst->pllstNext = NULL;
// Link this entry to the library list chain
if (glbl.pllstFirst == NULL)
glbl.pllstFirst = pllst;
else
{pllst->pllstNext = glbl.pllstFirst;
glbl.pllstFirst = pllst;
}
}
// Add Entry to Module List
VOID NEAR BuildModuleList (pszModule, fLibrary)
PCHAR pszModule; // -->DLL or EXE name
BOOL fLibrary; // TRUE == DLL
// Define function data
{ PMLIST pmlst; // -->Module list entry
PLLIST pllst; // -->Library list entry
FILESTATUS fsts; // File status information
PCHAR pszDLL; // -->DLL in file name
PCHAR pszDot; // -->Dot in file name
CHAR szMap[CCHMAXPATH]; // Map file name
BOOL fMatch = FALSE; // TRUE == library name matches
// Ignore libraries (.DLLs) that are not expected
if (fLibrary && !glbl.fAllDLLs)
{pllst = glbl.pllstFirst;
while (pllst != NULL)
{if ((pszDLL = strrchr (pszModule, '\\')) != NULL)
pszDLL++;
else pszDLL = pszModule;
if (strstr (pszDLL, pllst->pszName) != NULL)
{fMatch = TRUE;
break;
}
pllst = pllst->pllstNext;
}
if (! fMatch) return;
}
// Allocate a block for the module list entry
pmlst = (PMLIST) malloc (sizeof (*pmlst));
// Issue an error if out of memory
if (pmlst == NULL)
{printf
("\nOut of memory at %s:%d\n", __FILE__, __LINE__);
exit (1);
}
// Allocate a block for the module name
pmlst->pszName = (PCHAR) malloc (strlen (pszModule) + 1);
// Issue an error if out of memory
if (pmlst->pszName == NULL)
{printf
("\nOut of memory at %s:%d\n", __FILE__, __LINE__);
exit (1);
}
// Initialize the module list entry
strcpy (pmlst->pszName, pszModule);
strupr (pmlst->pszName);
pmlst->pmlstNext = NULL;
pmlst->pflstFirst = NULL;
// Link this entry to the module list chain
if (glbl.pmlstFirst == NULL)
glbl.pmlstFirst = pmlst;
else
{pmlst->pmlstNext = glbl.pmlstFirst;
glbl.pmlstFirst = pmlst;
}
// Build accumulator buckets for this module
BuildBuckets (pmlst);
// If there's a map file, build the
// function list for this module
strcpy (szMap, pmlst->pszName);
if ((pszDot = strrchr (szMap, '.')) == NULL)
strcat (szMap, ".MAP");
else strcpy (pszDot, ".MAP");
if (DosQPathInfo (szMap, FIL_STANDARD,
(PBYTE) &fsts, sizeof (fsts), 0L) == 0)
BuildFunctionList (pmlst, szMap);
}
// Build Selector-to-Segment Index
VOID NEAR BuildSelectorIndex (VOID)
// Define function data
{ USHORT iselix; // Index into selector table
// Allocate a selector-to-segment index
glbl.pselix = (PSELINDEX) malloc (sizeof (*glbl.pselix));
// Issue an error if out of memory
if (glbl.pselix == NULL)
{printf
("\nOut of memory at %s:%d\n", __FILE__, __LINE__);
exit (1);
}
// Initialize selector-to-segment index
for (iselix = 0; iselix < MAX_SELECTORS; iselix++)
glbl.pselix->apbckt[iselix] = NULL;
}
// Extract Options from Command Line
int NEAR ExtractOptions (argc, argv)
int argc; // Argument count
char *argv[]; // Argument values
// Define function data
{int iarg; // First non-option argument
FILESTATUS fsts; // File status information
USHORT cbBucket; // Bucket size
// Verify arguments
if (argc < 2)
{printf
("\nUsage is: %s [<options>] <program> [<args>]\n",
argv[0]);
printf ("<options> are:\n");
printf
("-B:nn -- size of accumulator buckets\n");
printf
("-L:xxxxxxxx.xxx -- DLL to be profiled\n");
printf
(" If ALL, all DLLs profiled\n");
printf
(" May be repeated as necessary\n");
printf
("-O:xxxxxxxx.xxx -- output file for profile data\n");
printf ("<program> is program to be profiled\n");
printf ("<args> are its arguments, if any\n");
exit (1);
}
// Set default argument values
glbl.cbBucket = NOMINAL_BUCKET_SIZE;
strcpy (glbl.szResults, "NUL");
// Scan arguments looking for options
for (iarg = 1; iarg < argc; iarg++)
// Stop if argument is not an option
{if ((argv[iarg][0] != '-') &&
(argv[iarg][0] != '/'))
break;
if (strlen (argv[iarg]) < 4)
break;
if (argv[iarg][2] != ':')
break;
// Process option
switch (argv[iarg][1])
// -B:nn --- Bucket size
{case 'b':
case 'B':
cbBucket = atoi (&(argv[iarg][3]));
if (cbBucket < MINIMUM_BUCKET_SIZE)
printf
("\nWARNING: Bucket size must be >= %d\n",
MINIMUM_BUCKET_SIZE);
else glbl.cbBucket = cbBucket;
break;
// -L:xxxxxxxx.xxx --- DLL name
case 'l':
case 'L':
if (DosQPathInfo (&(argv[iarg][3]),
FIL_NAMEISVALID, (PBYTE) &fsts,
sizeof (fsts), 0L) != 0)
printf
("\nWARNING: %s is an invalid DLL name\n",
&(argv[iarg][3]));
else
BuildLibraryList (&(argv[iarg][3]));
break;
// -O:xxxxxxxx.xxx --- Output file name
case 'o':
case 'O':
if (DosQPathInfo (&(argv[iarg][3]),
FIL_NAMEISVALID, (PBYTE) &fsts,
sizeof (fsts), 0L) != 0)
printf
("\nWARNING: %s is an invalid file name\n",
&(argv[iarg][3]));
else
strcpy (glbl.szResults, &(argv[iarg][3]));
break;
// Unknown option
default:
printf
("\nWARNING: %s is invalid and is ignored\n",
argv[iarg]);
break;
}
}
return iarg;
}
// Free All Acquired Data Structures
VOID NEAR FreeDataStructures (VOID)
// Define function data
{ USHORT iselix; // Index to selectors
PLLIST pllst, pllstX; // -->DLL list entry
PMLIST pmlst, pmlstX; // -->Module list entry
PFLIST pflst, pflstX; // -->Function list entry
// Free all accumulator buckets and
// the selector-to-segment index
for (iselix = 0; iselix < MAX_SELECTORS; iselix++)
{if (glbl.pselix->apbckt[iselix] != NULL)
free (glbl.pselix->apbckt[iselix]);
}
free (glbl.pselix);
// Free each module list entry and
// its associated function list entries
pmlst = glbl.pmlstFirst;
while (pmlst != NULL)
{pflst = pmlst->pflstFirst;
while (pflst != NULL)
{pflstX = pflst;
pflst = pflst->pflstNext;
free (pflstX->pszName);
free (pflstX);
}
pmlstX = pmlst;
pmlst = pmlst->pmlstNext;
free (pmlstX->pszName);
free (pmlstX);
}
// Free list of expected DLLs
pllst = glbl.pllstFirst;
while (pmlst != NULL)
{pllstX = pllst;
pllst = pllst->pllstNext;
free (pllstX->pszName);
free (pllstX);
}
}
// Output Profiling Results
VOID NEAR PrintResults (VOID)
// Define function data
{USHORT iselix; // Index into selector table
PBUCKETS pbckt; // -->Accumulator buckets
USHORT offv; // Precomputed offset value
PMLIST pmlst; // -->Module list entry
PFLIST pflst; // -->Function list entry
USHORT iBucket; // Index into buckets
CHAR szFunction[CCHMAXPATH]; // Function name
PCHAR pszName; // -->Actual module name
FILE *fResults; // Output results file
// Open file to hold results
fResults = fopen (glbl.szResults, "wt");
// Issue an error if the results file could not be opened
if (fResults == 0)
{printf
("\nWARNING: Unable to open output results file %s\n",
glbl.szResults);
return;
}
// Output total unknown, known counts
fprintf (fResults, "Unknown, Known\n");
fprintf (fResults, "%ld, %ld\n",
glbl.lUnknown, glbl.lKnown);
// Leave room for expansion. If new output is required,
// we will replace the following dummy lines.
fprintf (fResults, "This line is reserved for future use\n");
fprintf (fResults, "This line is reserved for future use\n");
// For each selector that is in use...
fprintf (fResults,
"Module, SEG:OFF, Count, Module, Function, Count\n");
for (iselix = 0; iselix < MAX_SELECTORS; iselix++)
{if (glbl.pselix->apbckt[iselix] != NULL)
// For each non-zero accumulator bucket...
{pbckt = glbl.pselix->apbckt[iselix];
for (iBucket = 0; iBucket < pbckt->cBuckets;
iBucket++)
{if (pbckt->alBucket[iBucket] != 0)
// Initialize function as module name in case we
// don't have access to any function information
{offv = iBucket * glbl.cbBucket;
if ((pszName = strrchr (
pbckt->pmlst->pszName, '\\')) != NULL)
pszName++;
else
pszName = pbckt->pmlst->pszName;
sprintf (szFunction, "%s[%04X:%04X]",
pszName, pbckt->segv, offv);
// Scan each function in each module to see if
// this accumulator bucket lies within it
pmlst = glbl.pmlstFirst;
while (pmlst != NULL)
{pflst = pmlst->pflstFirst;
while (pflst != NULL)
{if ((iselix == pflst->selv)
&& (offv >= pflst->offv)
&& (offv < (pflst->offv + pflst->lenv)))
{sprintf (szFunction, "%s+%04X",
pflst->pszName,
offv - pflst->offv);
goto OutputCount;
}
pflst = pflst->pflstNext;
}
pmlst = pmlst->pmlstNext;
}
// Output bucket count
OutputCount:
fprintf (fResults,
"%s, %04X:%04X, %ld, %s, %s, %ld\n",
pbckt->pmlst->pszName,
pbckt->segv,
offv,
pbckt->alBucket[iBucket],
pbckt->pmlst->pszName,
szFunction,
pbckt->alBucket[iBucket]);
}
}
}
}
fclose (fResults); // Close results file
}
// Profile Requested Program
USHORT NEAR ProfileProgram (pszPgm)
PCHAR pszPgm; // -->Name of program to profile
// Define function data
{USHORT selv; // Selector value
CHAR szDLL[CCHMAXPATH]; // Name of DLL being loaded
PCHAR pszDLL = szDLL; // -->DLL name
PCHAR pszName; // -->Module name
PCHAR pszTime; // -->Current time string
time_t lTime; // Current time and date
// Initialize profiling
glbl.ptb.cmd = PTRACE_CMD_STOP;
glbl.ptb.mte = 0;
DosPTrace ((PBYTE) &glbl.ptb);
time (&lTime); pszTime = ctime (&lTime); pszTime[24] = 0;
printf ("\n%s Starting %s", pszTime, pszPgm);
// Build module list entry for .EXE
BuildModuleList (pszPgm, FALSE);
// Profile analysis loop
while (TRUE)
// Restart program after event
{glbl.ptb.tid = 0;
glbl.ptb.cmd = PTRACE_CMD_GO;
DosPTrace ((PBYTE) &glbl.ptb);
// Analyze event code
switch ((SHORT) glbl.ptb.cmd)
// Signal
{case PTRACE_EVENT_SIGNAL:
// Get CS:IP
glbl.ptb.cmd = PTRACE_CMD_READ_REGISTERS;
DosPTrace ((PBYTE) &glbl.ptb);
// Convert CS to an index [0..8191]
glbl.ptb.rCS >>= 3;
// Increment accumulator bucket. If we can
// attribute the CS:IP, we increment both the
// accumulator bucket and the total count of
// "known" hits. Otherwise, we just increment
// the total count of "unknown" hits.
if (glbl.pselix->apbckt[glbl.ptb.rCS] != 0)
{++(glbl.pselix->apbckt[glbl.ptb.rCS]->
alBucket[glbl.ptb.rIP/glbl.cbBucket]);
++glbl.lKnown;
}
else ++glbl.lUnknown;
break;
// Process termination
case PTRACE_EVENT_DYING:
time (&lTime);
pszTime = ctime (&lTime);
pszTime[24] = 0;
printf ("\n%s Terminating %s", pszTime, pszPgm);
return 0;
// GP fault
case PTRACE_EVENT_GP_FAULT:
// If possible, convert CS:IP to
// SEG:OFF in a known .EXE or .DLL
selv = glbl.ptb.segv >> 3;
if (glbl.pselix->apbckt[selv] != NULL)
{glbl.ptb.segv =
glbl.pselix->apbckt[selv]->segv;
pszName =
glbl.pselix->apbckt[selv]->pmlst->pszName;
}
else pszName = pszPgm;
// Log GP fault
time (&lTime);
pszTime = ctime (&lTime);
pszTime[24] = 0;
printf ("\n%s GP fault %04X in %s at %04X:%04X",
pszTime, glbl.ptb.value, pszName,
glbl.ptb.segv, glbl.ptb.offv);
return glbl.ptb.value;
// Library load
case PTRACE_EVENT_LIBRARY_LOAD:
// Determine library name
glbl.ptb.cmd = PTRACE_CMD_GET_LIBNAME;
glbl.ptb.segv = FP_SEG (pszDLL);
glbl.ptb.offv = FP_OFF (pszDLL);
DosPTrace ((PBYTE) &glbl.ptb);
glbl.ptb.mte = glbl.ptb.value;
// Build module list entry for .DLL
BuildModuleList (strupr (pszDLL), TRUE);
// Log library load
time (&lTime);
pszTime = ctime (&lTime);
pszTime[24] = 0;
printf ("\n%s Loading %s", pszTime, szDLL);
break;
// Thread termination
case PTRACE_EVENT_THREAD_DEAD:
time (&lTime);
pszTime = ctime (&lTime);
pszTime[24] = 0;
printf ("\n%s Terminating thread %u in %s",
pszTime, glbl.ptb.tid, pszPgm);
break;
// DosPTrace Error
case PTRACE_EVENT_ERROR:
time (&lTime);
pszTime = ctime (&lTime);
pszTime[24] = 0;
printf ("\n%s DosPtrace error %u in %s",
pszTime, glbl.ptb.value, pszPgm);
return glbl.ptb.value;
// Ignore others
break:
return;
}
// Restart program until it hurts!
}
// End of profiling
}
// Thread to Signal Profiled Program
VOID FAR Signaller (pCB)
PVOID pCB; // Dummy control block
// Define function data
{ USHORT idErr; // Error code
// Signal profiled program and wait for the sample interval
while (TRUE)
{idErr = DosFlagProcess (glbl.ptb.pid,
FLGP_PID, PFLG_A, 0);
DosSleep (SAMPLE_INTERVAL);
}
} // Terminate thread
// Start Program to be Profiled
VOID NEAR StartProgram (argc, argv, iarg, pszPgm)
int argc; // Argument count
char *argv[]; // Argument values
int iarg; // Index to program name
PCHAR pszPgm; // -->Area program name returned
// Define function data
{ STARTDATA stdata; // DosStartSession data
USHORT idErr; // Error code
CHAR szArgs[CCHMAXPATH]; // Program arguments
USHORT idSession; // Session id
// Extract program name from arguments
strcpy (pszPgm, argv[iarg]);
strupr (pszPgm);
if (strstr (pszPgm, ".EXE") == NULL)
strcat (pszPgm, ".EXE");
// Build argument string for program to be profiled
szArgs[0] = 0;
for (iarg++; iarg < argc; iarg++)
{if ((strlen (szArgs) +
strlen (argv[iarg]) + 1) >= sizeof (szArgs))
break;
strcat (szArgs, argv[iarg]);
strcat (szArgs, " ");
}
// Start program being profiled
stdata.Length = sizeof (stdata);
stdata.Related = TRUE;
stdata.FgBg = FALSE;
stdata.TraceOpt = TRUE;
stdata.PgmTitle = NULL;
stdata.PgmName = pszPgm;
stdata.PgmInputs = szArgs;
stdata.TermQ = NULL;
stdata.Environment = NULL;
stdata.InheritOpt = TRUE;
idErr = DosQAppType (pszPgm, &stdata.SessionType);
stdata.IconFile = NULL;
stdata.PgmHandle = (ULONG) NULL;
stdata.PgmControl = 2;
stdata.InitXPos = stdata.InitYPos = 0;
stdata.InitXSize = stdata.InitYSize = 0;
idErr = DosStartSession (
&stdata, &idSession, &glbl.ptb.pid);
// Issue an error if the program could not be started
if (idErr != 0)
{printf ("\n%s could not be started (error %u)\n",
pszPgm, idErr);
exit (idErr);
}
}